/*
 * Copyright (c) 2011 Junichiro Okuno
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Junichiro Okuno.
 */

package tcp_net.mail;

import tcp_net.*;
import tcp_net.management.user_account.*;
import tcp_net.mail.debug.DebugInfo;
import tcp_net.management.EmailServerManager;

import java.util.List;
import java.util.ArrayList;

/**
	This class provides a mailbox file manager for each registered user  
	@author J. Okuno
*/
public class MailBoxFileManager
{
	/**
		Debug information
	*/
	private final static boolean DEBUG = DebugInfo.MAIL_BOX_FILE_MANAGER_DEBUG;
	
	/**
		An email server manager
	*/
	private EmailServerManager email_manager;
	
	/**
		A user account manager
	*/
	private UserAccountManager ua_manager;
	
	/**
		An email user account
	*/
	private UserAccount user; 
	
	/**
		An email address of origin
	*/
	private String mail_from_address;
	
	/**
		A user name of an email account
	*/
	private String userName;
	
	/**
		true if a mailbox is blocked
	*/
	private boolean blocked;
	
	/**
		A list of email address to the destnations
	*/
	private List<String> recipt_addresses;
	
	/**
		Init the mailbox file manager
		@param man for managing email servers (Both SMTP and POP3)
	*/
	public MailBoxFileManager(EmailServerManager man)
	{
		userName = null;
		blocked = false;
		email_manager = man;
		ua_manager = email_manager.getUserAccountManager();
		user = null;
		
		recipt_addresses = new ArrayList<String>();
		DebugInfo.print(DEBUG,"MailBoxFileManager created");
	}
	
	/**
		This method adds a new to the ua_manager list
		@param usr a registered user name
		@return true if the user is added 
	*/
	public boolean addUserName(String usr) 
	{
		DebugInfo.print(DEBUG,"addUserName() started");
		
		//search user account 
		if(usr!=null && !usr.equals(""))
		{
			DebugInfo.print(DEBUG,"is a user there?: "+usr);
			
			if(usr==null) 
			{
				DebugInfo.print(DEBUG,"usr is null");
				return false;
			}
			
			if(ua_manager==null) 
			{
				DebugInfo.print(DEBUG,"UserAccountManager is null");
				return false;
			}
				
			user = ua_manager.getUserAccount(usr);
			
			//found
			if(user != null && user.isEnabled())
			{
				DebugInfo.print(DEBUG,"User found: "+userName);
				userName = usr;
				return true;
			}
		}
			//not found
			DebugInfo.print(DEBUG,"Unable to find a user");
			return false;	
	}
	
	/**
		This method provides a password of the email user account
		@return a password from the registered user (The user name must be provided by addUserName() method)
	*/
	public synchronized String getPassword()
	{
		DebugInfo.print(DEBUG,"getPassword() started");
		//search user account 
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
		
			//found
			if(user != null && user.isEnabled())
			{
				//find out the passworkd
				DebugInfo.print(DEBUG,"Password found: "+user.getPassword());
				return user.getPassword();
			}
		}
		
		//not found
		DebugInfo.print(DEBUG,"Unabled to identify password");
		return null;
	}
	
	/* For POP3 Server operations */
	
	/**
		This method locks the mailbox
		@return true if the mail drop is locked
	*/
	public synchronized boolean lockMaildrop()
	{
		//lock user's mail drop
		blocked = true;
		return blocked;
	}
	
	/**
		This method unlocks the mailbox
		@return true if the mail drop is locked
	*/
	public synchronized boolean unlockMaildrop()
	{
		//lock user's mail drop
		blocked = false;
		return blocked;
	}
	
	/**
		This method calc. the no. of msgs in the maildrop (excludes delete marled msgs)
		@return a number of messages
	*/
	public synchronized int getNoOfMessages() 
	{ 
		DebugInfo.print(DEBUG,"Calc. no. of messages in the maildrop."); 
		
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			return user.countNoOfMessages();
		}
		
		return 0; 
	}
	
	/**
		This method calc. the size of the maildrip in octs (excludes delete marled msgs)
		@return a size of the maildrop in octs
	*/
	public synchronized int getMaildropSize() 
	{ 
		DebugInfo.print(DEBUG,"Calc. the size of the maildrop.");
		
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			
			if(user!=null)
			{
				return user.getAllMessageSize();
			}
		}
		
		DebugInfo.print(DEBUG,"userName is null");
		return 0; 
	}
	 
	/**
		This method calc. the size of the message in octs
		@param msg_no a message number
		@return a size of the message in octs
	*/
	public synchronized int getMessageSize(int msg_no)
	{
		DebugInfo.print(DEBUG,"Calc. the size of message in the maildrop.");
		
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			
			if(user!=null)
			{
				if(user.getMessagesNotDeleted().size()>msg_no)
				{
					Message m = user.getMessagesNotDeleted().get(msg_no);
			
					if(m!=null)
					{
						return m.getMsgSize();
					}
				}
			}
		}
		
		DebugInfo.print(DEBUG,"No message");
		return -1;
	}
	
	/**
		This method gets an email message (excludes delete marled msgs)
		@param msg_no a message number
		@return an email message
	*/
	public synchronized String getEntireMessage(int msg_no)
	{
		if(userName!=null)
		{
			DebugInfo.print(DEBUG,"Requesting an entire message.");
			
			user = ua_manager.getUserAccount(userName);
			//String msg = user.getMessagesNotDeleted().get(msg_no).getMsgText()+"\n.";			
			String msg = user.getMessagesNotDeleted().get(msg_no).getMsgText();
			
			//if(msg.endsWith(".")) { msg = msg.substring(0, msg.length()-2); }
			DebugInfo.print(DEBUG,"Entire message: "+msg);
							
			return msg; 
		}
		
		DebugInfo.print(DEBUG,"UserName is null");
		return null;
	}
	
	/**
		This method mark message delete
		@param msg_no a message number
		@return true if the message is marked to be delete
	*/
	public synchronized boolean markMessageDelete(int msg_no)
	{
		DebugInfo.print(DEBUG,"Mark the message deleted.");
		
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			user.getMessagesNotDeleted().get(msg_no).setDeleted(true);
			
			return true;
		}
		
		DebugInfo.print(DEBUG,"UserName is null");
		return false;
	}
	
	
	/**
		This method unmark all the messages in a mailbox
		Unmark all the messages delete marked
	*/
	public synchronized void reset()
	{
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			
			for(Message m : user.getMessages())
			{
				m.setDeleted(false);
			}
		
			DebugInfo.print(DEBUG,"Unmark all the messages delete marked."); 
		}
		else
		{
			DebugInfo.print(DEBUG,"userName is null");	
		}
	}
	
	/**
		This method remove the delete marked messages
	*/
	public synchronized void deleteMarkedMessages()
	{
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			user.removeDeleteMessages();
		
			DebugInfo.print(DEBUG,"Delete marked messages removed.");
		}
		else
		{
			DebugInfo.print(DEBUG,"userName is null");
		}
	}
	
	/**
		This method returns a lines of header and body of an email (For top command)
		@param msg an email message
		@param n a number of lines requesting
		@return a list of header lines
	*/
	public synchronized List<String> getHeadersOfMessage(int msg, int n)
	{
		DebugInfo.print(DEBUG,"Top requested.");
		
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			List<String> header_lines = user.getMessagesNotDeleted().get(msg).getMsgText(n);
		
			return header_lines;
		}
		
		DebugInfo.print(DEBUG,"userName is null");
		return null;
	}
	
	//for uidl (unique id listing)
	/**
		This method returns a unique email message ID
		@param msg an email message
		@return an unique message id
	*/
	public synchronized String getUniqueId(int msg) 
	{
		DebugInfo.print(DEBUG,"Unique id listing requested."); 
		
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			return user.getMessagesNotDeleted().get(msg).getUniqueId();
		}
		
		DebugInfo.print(DEBUG,"userName is null");
		return null;
	}
	
	/**
		This method returns a list of unique email message IDs
		@return a list of email message unique ids
	*/
	public synchronized List<String> getAllUniqueIds()
	{
		List<String> uids = new ArrayList<String>();
		
		if(userName!=null)
		{
			user = ua_manager.getUserAccount(userName);
			
			for(Message m : user.getMessagesNotDeleted())
			{
				uids.add(m.getUniqueId());
			}
			
			DebugInfo.print(DEBUG,"Assing unique ids to all messages requested.");
		}
		else
		{
			DebugInfo.print(DEBUG,"userName is null");
		}
		
		return uids;
	}
	/* End of POP3 Server operations */
	
	
	/* For SMTP Server operations */
	
	/**
		This method adds a recept email address
		@param email_address a recept email address 
	*/
	public synchronized void addReceptAddresses(String email_address)
	{
		DebugInfo.print(DEBUG,"Email address: "+email_address+" added to recpt_addresses list");
		recipt_addresses.add(email_address);
	}
	
	/**
		This method provides a list of recept email addresses
		@return a list of recept email addresses 
	*/
	public synchronized List<String> getReceptAddresses() 
	{ 
		
		DebugInfo.print(DEBUG,"getReceptAddresses() requested with followings");
		
		for(String str : recipt_addresses)
		{
			DebugInfo.print(DEBUG,"Address: "+str);
		}
		
		return recipt_addresses; 
	}
	
	/**
		This method clear all recp email addresses
	*/
	public synchronized void clearReceptAddresses() 
	{ 
		DebugInfo.print(DEBUG,"recipt_addresses list is cleared");
		recipt_addresses.clear(); 
	}
	
	/**
		This method sets a new email address from the connected client
		@param address an email address from the connected client
	*/
	public synchronized void setMailFromAddress(String address) { mail_from_address = address; }
	
	/**
		This method provides an email address from already connected client
		@return an email address from the connected client
	*/
	public synchronized String getMailFromAddress() { return mail_from_address; }
	
	/**
		This method provides a user name from the email address
		@param email_address
		@return a username from the email address
	*/
	public synchronized String getUserName(String email_address)
	{
		email_address = "<" + email_address + ">";
		
		DebugInfo.print(DEBUG,"getUserName(): "+email_address);
		
		CommandAnalyser ana = new CommandAnalyser();
		return ana.getSubstring(email_address, '<','@');
	}
	
	/**
		This method provides a domain name from the email address
		@param email_address 
		@return a domain name from the email address
	*/
	public synchronized String getDomain(String email_address)
	{
		email_address = "<" + email_address + ">";
		
		DebugInfo.print(DEBUG,"getDomain(): "+email_address);
		
		CommandAnalyser ana = new CommandAnalyser();
		return ana.getSubstring(email_address, '@','>');
	}
	
	/**
		This method checks if an email user account is valid
		@param user_name
		@return true if a user name is registered and enabled
	*/
	public synchronized boolean isUserValid(String user_name)
	{
		user = ua_manager.getUserAccount(user_name);
		
		if(user!=null && user.isEnabled()) 
		{ 
			DebugInfo.print(DEBUG,user_name+" is valid.");
			return true; 
		}
		
		else 
		{ 
			DebugInfo.print(DEBUG,user_name+" is not valid");
			return false; 
		}
	}
	
	/**
		This method delivers the message
		@param usr an email user account
		@param msg an email message
	*/
	public synchronized void deliverMessage(String usr, String msg)
	{
		user = ua_manager.getUserAccount(usr);
		
		if(user.isEnabled())
		{
			DebugInfo.print(DEBUG, usr+"\'s message: "+msg+" is delivered.");
			user.addMessage(msg);
		}
	}
	
	/* End of SMTP Server operations */
}